/***************************************************************************
   Filename:    Ball.cpp
   Author:      Josh Partridge
   Description: The Ball class acts as the ball in Breakout
****************************************************************************/
#include "Ball.h"

/************************************************************************************
 Name:               Ball - default constructor
 Description:        Only explicitly defined because game must call this constructor
                     first when initializing Ball as a private member. During Game's
                     own constructor, we call a different constructor of Ball.
*************************************************************************************/
Ball::Ball()
{
}

/************************************************************************************
 Name:               Ball - constructors
 Description:        initialize various attributes of Ball
 Input:
              x_pos: double representing x position of ball
              y_pos: double representing x position of ball
                  c: ink representing color of the ball
                  s: pointer to game's score variable
*************************************************************************************/
Ball::Ball(unsigned int* s)
{
    x = 58;
    y = 32;
    theta = 135;
    magnitude = 0;
    color = white;
    score = s;
}

Ball::Ball(ink c, unsigned int* s)
{
    x = 58;
    y = 32;
    theta = 135;
    magnitude = 0;
    color = c;
    score = s;
}

Ball::Ball(double x_pos, double y_pos, unsigned int* s)
{
    x = x_pos;
    y = y_pos;
    theta = 135;
    magnitude = 0;
    color = white;
    score = s;
}

Ball::Ball(double x_pos, double y_pos, ink c, unsigned int* s)
{
    x = x_pos;
    y = y_pos;
    theta = 135;
    magnitude = 0;
    color = c;
    score = s;
}

/************************************************************************************
 Name:               setTheta
 Description:        setter for theta attribute (double)
 Input:
                  t: double representing new angle
*************************************************************************************/
void Ball::setTheta(double t)
{
    while(t<0)
    {
        t += 360;
    }
    t = fmod(t, 360);

    //round to neares 6.25 degrees
    t -= fmod(t, 6.25);

    switch((int)t)
    {
        case 0:
        case 90:
        case 180:
        case 270:

        t += 6.25;

        case 360:

        t = 5.25;

        default:;
    }
    //keep from being too vertical or horizontal
    if(t<angleInc*1.5)
        t=angleInc*1.5;
    else if(t<90 && t>90 - angleInc*1.5)
        t=90-angleInc*1.5;
    else if(t>90 && t<90 + angleInc*1.5)
        t=90+angleInc*1.5;
    else if(t<180 && t>180-angleInc*1.5)
        t=180-angleInc*1.5;
    else if(t>180 && t<180+angleInc*1.5)
        t=180+angleInc*1.5;
    else if(t<270 && t>270-angleInc*1.5)
        t=270-angleInc*1.5;
    else if(t>270 && t<270+angleInc*1.5)
        t=270+angleInc*1.5;
    else if(t<360 && t>360-angleInc*1.5)
        t=360-angleInc*1.5;

    theta = t;
}

 /************************************************************************************
 Name:               getTheta
 Description:        getter for theta attribute (double)
 Output:
             return: double representing ball's theta attribute
*************************************************************************************/
double Ball::getTheta()
{
    return theta;
}

/************************************************************************************
     Name:               setMagnitue
     Description:        setter for magnitude attribute
     Input:
                  mag: new magnitude (double)
*************************************************************************************/
void Ball::setMagnitude(double mag)
{
    magnitude = fabs(mag);
    if (magnitude > 10)
        magnitude = 10;
}

/************************************************************************************
 Name:               setColor
 Description:        setter for color attribute (ink)
 Input:
                  c: ink representing new color
*************************************************************************************/
void Ball::setColor(ink c)
{
    color = c;
}

/************************************************************************************
 Name:               teleport
 Description:        moves ball without collision testing
 Input:
              x_pos: double representing new x coordinate
              y_pos: double representing new y coordinate
*************************************************************************************/
void Ball::teleport(double x_pos, double y_pos)
{
    if(x_pos<4)
        x_pos=4;
    else if (x_pos>115)
        x_pos=115;
    if(y_pos<4)
        y_pos=4;

    x = x_pos;
    y = y_pos;
}

/************************************************************************************
 Name:               xCollide
 Description:        used by move() to detect collision with anything in the x-axis
 Input:
                 dx: double representing attempted change in x position
          paddlePos: an integer value representing the maximum value
             bricks: address of the vector holding the level's bricks
              brush: address of the game's brush object
                     (for updating brick's appearance)
 Output:
             return: bool value testing whether anything collided
             bricks: address of the vector holding the level's bricks
              brush: address of the game's brush object
 *************************************************************************************/
bool Ball::xCollide(double dx, int paddlePos, vector<Brick>& bricks, Brush& brush)
{
    if(x+dx<4 || x+dx>116)
        return true;
    if((int)y<=34 && (int)y+1>= 33 && (int)(x+dx)>=paddlePos && (int)(x+dx)<=paddlePos+16)
        return true;
    for(unsigned int i=0; i < bricks.size(); i++)
    {
        RECT box = bricks[i].getBounds();
        if((int)(x+dx)+1>=box.left && (int)(x+dx)<=box.right && (int)y<=box.bottom && (int)y+1>=box.top)
        {
            bricks[i].takeDamage(1, *score);
            bricks[i].display(brush);
            if(!bricks[i].isAlive())
                bricks.erase(bricks.begin() + i);
            return true;
        }
    }
    return false;
}

/************************************************************************************
 Name:               yCollide
 Description:        used by move() to detect collision with anything in the y-axis
 Input:
                 dy: double representing attempted change in y position
          paddlePos: an integer value representing the maximum value
          paddleVel: paddle's velocity, for affecting ball trajectory
             bricks: address of the vector holding the level's bricks
              brush: address of the game's brush object
                     (for updating brick's appearance)
 Output:
             return: bool value testing whether anything collided
             bricks: address of the vector holding the level's bricks
              brush: address of the game's brush object
*************************************************************************************/
bool Ball::yCollide(double dy, int paddlePos, int paddleVel, vector<Brick>& bricks, Brush& brush)
{
    if(y-dy<3)
        return true;
    if((int)x+1>=paddlePos && (int)x<=paddlePos+16 && ((int)(y-dy)+1>=33 && (int)(y-dy)<=34))
    {
        if(GetAsyncKeyState(VK_SPACE))
        {
            double diff=fabs(270-theta);
            diff*=0.66;
            if(theta<270)
                setTheta(270-diff);
            else
                setTheta(270+diff);
        }
        else
        {
            setTheta(theta + paddleVel*3);
        }
        return true;
    }
    for(unsigned int i=0; i < bricks.size(); i++)
    {
        RECT box = bricks[i].getBounds();
        if((int)x+1>=box.left && (int)x<=box.right && (int)(y-dy)<=box.bottom && (int)(y-dy)+1>=box.top)
        {
            bricks[i].takeDamage(1, *score);
            bricks[i].display(brush);
            if(!bricks[i].isAlive())
                bricks.erase(bricks.begin() + i);
            return true;
        }
    }
    return false;
}


/************************************************************************************
 Name:               move
 Description:        determines ball movement both before and after release from paddle
 Input:
             bricks: address of the vector holding the level's bricks
          paddlePos: an integer value representing the maximum value
          paddleVel: paddle's velocity, for affecting ball trajectory
              brush: address of the game's brush object
                     (for updating brick's appearance)
 Output:
             return: bool value testing whether ball crossed the screen's lower bound
             bricks: address of the vector holding the level's bricks
              brush: address of the game's brush object
*************************************************************************************/
bool Ball::move(vector<Brick>& bricks, int paddlePos, int paddleVel, Brush& brush)
{
    if(magnitude<=0)
    {
        teleport(paddlePos + 8, 32);
        if(paddleVel)
        {
            setTheta(90 - paddleVel*10);
        }

        if(GetAsyncKeyState(VK_SPACE))
        {
            magnitude=2;
        }
    }
    else
    {
        setTheta(theta);
        double dx=magnitude*cos(theta*PI/180);
        double dy=magnitude*sin(theta*PI/180)*0.75;
        if(yCollide(dy, paddlePos, paddleVel, bricks, brush))
        {
            setTheta(theta * -1);
        }
        else
        {
            y-=dy;
        }
        if(xCollide(dx, paddlePos, bricks, brush))
        {
            setTheta(180-theta);
        }
        else
        {
            x+=dx;
        }
        if(y>37)
            return true;
    }
    return false;
}

/************************************************************************************
 Name:               draw
 Description:        method for displaying itself to the screen
 Input:
              brush: address of the game's brush object
                     (for drawing in new position)
*************************************************************************************/
void Ball::draw(Brush &brush)
{
    if(y<37)
    {
        brush.setColor(color);
        brush.plot((int)x, (int)y, 'O');
    }
}

/************************************************************************************
 Name:               erase
 Description:        method for erasing old position
 Input:
              brush: address of the game's brush object
                     (for drawing in new position)
*************************************************************************************/
void Ball::erase(Brush &brush)
{
    if(y<37)
    brush.plot((int)x, (int)y, ' ');
}

